{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "tf_records.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [
        "pL--_KGdYoBz"
      ],
      "toc_visible": true
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.5"
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "pL--_KGdYoBz"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "both",
        "colab_type": "code",
        "id": "uBDvXpYzYnGj",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "HQzaEQuJiW_d"
      },
      "source": [
        "# TFRecords と `tf.Example` の使用法"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hWk5m9autbk8",
        "colab_type": "text"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ja/r1/tutorials/load_data/tf-records.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ja/r1/tutorials/load_data/tf-records.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AvXexNxlLoFx",
        "colab_type": "text"
      },
      "source": [
        "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3pkUd_9IZCFO"
      },
      "source": [
        "データの読み込みを効率的にするには、データをシリアライズし、連続的に読み込めるファイルのセット（各ファイルは100-200MB）に保存することが有効です。データをネットワーク経由で流そうとする場合には、特にそうです。また、データの前処理をキャッシュする際にも役立ちます。\n",
        "\n",
        "TFRecord形式は、バイナリレコードの系列を保存するための単純な形式です。\n",
        "\n",
        "[プロトコルバッファ](https://developers.google.com/protocol-buffers/) は、構造化データを効率的にシリアライズする、プラットフォームや言語に依存しないライブラリです。\n",
        "\n",
        "プロトコルメッセージは`.proto`という拡張子のファイルで定義されます。メッセージ型を識別する最も簡単な方法です。\n",
        "\n",
        "`tf.Example`メッセージ（あるいはプロトコルバッファ）は、`{\"string\": value}`形式のマッピングを表現する柔軟なメッセージ型です。これは、TensorFlow用に設計され、[TFX](https://www.tensorflow.org/tfx/)のような上位レベルのAPIで共通に使用されています。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Ac83J0QxjhFt"
      },
      "source": [
        "このノートブックでは、`tf.Example`メッセージの作成、パースと使用法をデモし、その後、`tf.Example`メッセージをパースして、`.tfrecord`に書き出し、その後読み取る方法を示します。\n",
        "\n",
        "注：こうした構造は有用ですが必ずそうしなければならなというものではありません。[`tf.data`](https://www.tensorflow.org/r1/guide/datasets) を使っていて、それでもなおデータの読み込みが訓練のボトルネックである場合でなければ、既存のコードをTFRecordsを使用するために変更する必要はありません。データセットの性能改善のヒントは、 [Data Input Pipeline Performance](https://www.tensorflow.org/r1/guide/performance/datasets)を参照ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "WkRreBf1eDVc"
      },
      "source": [
        "## 設定"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Ja7sezsmnXph",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "import tensorflow as tf\n",
        "tf.enable_eager_execution()\n",
        "\n",
        "import numpy as np\n",
        "import IPython.display as display"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "e5Kq88ccUWQV"
      },
      "source": [
        "## `tf.Example`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VrdQHgvNijTi"
      },
      "source": [
        "###  `tf.Example`用のデータ型"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "lZw57Qrn4CTE"
      },
      "source": [
        "基本的には`tf.Example`は`{\"string\": tf.train.Feature}`というマッピングです。\n",
        "\n",
        "`tf.train.Feature`メッセージ型は次の3つの型のうち1つをとることができます（[.proto file](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/feature.proto)を参照）。一般的なデータ型の多くは、これらの型のいずれかに強制的に変換することができます。\n",
        "\n",
        "1. `tf.train.BytesList` (次の型のデータを扱うことが可能)\n",
        "  - `string`\n",
        "  - `byte`  \n",
        "1. `tf.train.FloatList` (次の型のデータを扱うことが可能)\n",
        "  - `float` (`float32`)\n",
        "  - `double` (`float64`)  \n",
        "1. `tf.train.Int64List` (次の型のデータを扱うことが可能)\n",
        "  - `bool`\n",
        "  - `enum`\n",
        "  - `int32`\n",
        "  - `uint32`\n",
        "  - `int64`\n",
        "  - `uint64`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_e3g9ExathXP"
      },
      "source": [
        "通常のTensorFlowの型を`tf.Example`互換の `tf.train.Feature`に変換するには、次のショートカット関数を使うことができます。\n",
        "\n",
        "どの関数も、1個のスカラー値を入力とし、上記の3つの`list`型のうちの一つを含む`tf.train.Feature`を返します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mbsPOUpVtYxA",
        "colab": {}
      },
      "source": [
        "# 下記の関数を使うと値を tf.Exampleと互換性の有る型に変換できる\n",
        "\n",
        "def _bytes_feature(value):\n",
        "    \"\"\"string / byte 型から byte_listを返す\"\"\"\n",
        "    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))\n",
        "\n",
        "def _float_feature(value):\n",
        "    \"\"\"float / double 型から float_listを返す\"\"\"\n",
        "    return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))\n",
        "\n",
        "def _int64_feature(value):\n",
        "    \"\"\"bool / enum / int / uint 型から Int64_listを返す\"\"\"\n",
        "    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Wst0v9O8hgzy"
      },
      "source": [
        "注：単純化のため、このサンプルではスカラー値の入力のみを扱っています。スカラー値ではない特徴を扱う最も簡単な方法は、`tf.serialize_tensor`を使ってテンソルをバイナリ文字列に変換する方法です。TensorFlowでは文字列はスカラー値として扱います。バイナリ文字列をテンソルに戻すには、`tf.parse_tensor`を使用します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vsMbkkC8xxtB"
      },
      "source": [
        "上記の関数の使用例を下記に示します。入力が様々な型であるのに対して、出力が標準化されていることに注目してください。入力が、強制変換できない型であった場合、例外が発生します。（例：`_int64_feature(1.0)`はエラーとなります。`1.0`が浮動小数点数であるためで、代わりに`_float_feature`関数を使用すべきです）"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "hZzyLGr0u73y",
        "colab": {}
      },
      "source": [
        "print(_bytes_feature(b'test_string'))\n",
        "print(_bytes_feature(u'test_bytes'.encode('utf-8')))\n",
        "\n",
        "print(_float_feature(np.exp(1)))\n",
        "\n",
        "print(_int64_feature(True))\n",
        "print(_int64_feature(1))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "nj1qpfQU5qmi"
      },
      "source": [
        "メッセージはすべて`.SerializeToString` を使ってバイナリ文字列にシリアライズすることができます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5afZkORT5pjm",
        "colab": {}
      },
      "source": [
        "feature = _float_feature(np.exp(1))\n",
        "\n",
        "feature.SerializeToString()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "laKnw9F3hL-W"
      },
      "source": [
        "### `tf.Example` メッセージの作成"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "b_MEnhxchQPC"
      },
      "source": [
        "既存のデータから`tf.Example`を作成したいとします。実際には、データセットの出処はどこでも良いのですが、1件の観測記録から`tf.Example`メッセージを作る手順は同じです。\n",
        "\n",
        "1. 観測記録それぞれにおいて、各値は上記の関数を使って3種類の互換性のある型のうち1つだけを含む`tf.train.Feature`に変換する必要があります。\n",
        "\n",
        "1. 次に、特徴の名前を表す文字列と、#1で作ったエンコード済みの特徴量を対応させたマップ（ディクショナリ）を作成します。\n",
        "\n",
        "1. #2で作成したマップを[Featuresメッセージ](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/feature.proto#L85)に変換します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "4EgFQ2uHtchc"
      },
      "source": [
        "このノートブックでは、NumPyを使ってデータセットを作成します。\n",
        "\n",
        "このデータセットには4つの特徴量があります。\n",
        "- `False` または `True`を表す論理値。出現確率は等しいものとします。\n",
        "- `[0,5)`の範囲から一様にサンプリングした整数値。\n",
        "- 整数特徴量をインデックスとした文字列テーブルを使って生成した文字列特徴量\n",
        "- 標準正規分布からサンプリングした浮動小数点数。\n",
        "\n",
        "サンプルは上記の分布から独立して同じ様に分布した10,000件の観測記録からなるものとします。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "CnrguFAy3YQv",
        "colab": {}
      },
      "source": [
        "# データセットに含まれる観測結果の件数\n",
        "n_observations = int(1e4)\n",
        "\n",
        "# ブール特徴量 FalseまたはTrueとしてエンコードされている\n",
        "feature0 = np.random.choice([False, True], n_observations)\n",
        "\n",
        "# 整数特徴量  0以上 5未満の乱数\n",
        "feature1 = np.random.randint(0, 5, n_observations)\n",
        "\n",
        "# バイト文字列特徴量\n",
        "strings = np.array([b'cat', b'dog', b'chicken', b'horse', b'goat'])\n",
        "feature2 = strings[feature1]\n",
        "\n",
        "# 浮動小数点数特徴量 標準正規分布から発生\n",
        "feature3 = np.random.randn(n_observations)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "aGrscehJr7Jd"
      },
      "source": [
        "これらの特徴量は、`_bytes_feature`, `_float_feature`, `_int64_feature`のいずれかを使って`tf.Example`互換の型に強制変換されます。その後、エンコード済みの特徴量から`tf.Example`メッセージを作成できます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "RTCS49Ij_kUw",
        "colab": {}
      },
      "source": [
        "def serialize_example(feature0, feature1, feature2, feature3):\n",
        "    \"\"\"\n",
        "    Creates a tf.Example message ready to be written to a file.\n",
        "    ファイル出力可能なtf.Exampleメッセージを作成する\n",
        "    \"\"\"\n",
        "\n",
        "    # 特徴量名とtf.Example互換データ型との対応ディクショナリを作成\n",
        "\n",
        "    feature = {\n",
        "        'feature0': _int64_feature(feature0),\n",
        "        'feature1': _int64_feature(feature1),\n",
        "        'feature2': _bytes_feature(feature2),\n",
        "        'feature3': _float_feature(feature3),\n",
        "    }\n",
        "\n",
        "    # tf.train.Exampleを用いて特徴メッセージを作成\n",
        "\n",
        "    example_proto = tf.train.Example(features=tf.train.Features(feature=feature))\n",
        "    return example_proto.SerializeToString()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "XftzX9CN_uGT"
      },
      "source": [
        "例えば、データセットに`[False, 4, bytes('goat'), 0.9876]`という1つの観測記録があるとします。`create_message()`を使うとこの観測記録から`tf.Example`メッセージを作成し印字できます。上記のように、観測記録一つ一つが`Features`メッセージとして書かれています。`tf.Example` [メッセージ](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto#L88)は、この`Features` メッセージを包むラッパーに過ぎないことに注意してください。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "N8BtSx2RjYcb",
        "colab": {}
      },
      "source": [
        "# データセットからの観測記録の例\n",
        "\n",
        "example_observation = []\n",
        "\n",
        "serialized_example = serialize_example(False, 4, b'goat', 0.9876)\n",
        "serialized_example"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_pbGATlG6u-4"
      },
      "source": [
        "メッセージをデコードするには、`tf.train.Example.FromString`メソッドを使用します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "dGim-mEm6vit",
        "colab": {}
      },
      "source": [
        "example_proto = tf.train.Example.FromString(serialized_example)\n",
        "example_proto"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZHV-ff6YHtDG",
        "colab_type": "text"
      },
      "source": [
        "## TFRecordフォーマットの詳細\n",
        "\n",
        "TFRecordファイルにはレコードのシーケンスが含まれます。このファイルはシーケンシャル読み取りのみが可能です。\n",
        "\n",
        "それぞれのレコードには、データを格納するためのバイト文字列とデータ長、そして整合性チェックのためのCRC32C（Castagnoli多項式を使った32ビットのCRC）ハッシュ値が含まれます。\n",
        "\n",
        "各レコードのフォーマットは下記の通りです。\n",
        "\n",
        "    uint64 長さ\n",
        "    uint32 長さのマスク済みcrc32ハッシュ値\n",
        "    byte   data[長さ]\n",
        "    uint32 データのマスク済みcrc32ハッシュ値\n",
        "\n",
        "複数のレコードが結合されてファイルを構成します。CRCについては[ここ](https://en.wikipedia.org/wiki/Cyclic_redundancy_check)に說明があります。CRCのマスクは下記のとおりです。\n",
        "\n",
        "    masked_crc = ((crc >> 15) | (crc << 17)) + 0xa282ead8ul\n",
        "    \n",
        "注：TFRecordファイルを作るのに、`tf.Example`を使わなければならないということはありません。`tf.Example`は、ディクショナリをバイト文字列にシリアライズする方法の1つです。エンコードされた画像データや、（`tf.io.serialize_tensor`を使ってシリアライズされ、`tf.io.parse_tensor`で読み込まれる）シリアライズされたテンソルもあります。その他のオプションについては、`tf.io`モジュールを参照してください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "y-Hjmee-fbLH"
      },
      "source": [
        "## `tf.data`を使用したTFRecordファイル"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GmehkCCT81Ez"
      },
      "source": [
        "`tf.data`モジュールには、TensorFlowでデータを読み書きするツールが含まれます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "1FISEuz8ubu3"
      },
      "source": [
        "### TFRecordファイルの書き出し\n",
        "\n",
        "データをデータセットにする最も簡単な方法は`from_tensor_slices`メソッドです。\n",
        "\n",
        "配列に適用すると、このメソッドはスカラー値のデータセットを返します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mXeaukvwu5_-",
        "colab": {}
      },
      "source": [
        "tf.data.Dataset.from_tensor_slices(feature1)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "f-q0VKyZvcad"
      },
      "source": [
        "配列のタプルに適用すると、タプルのデータセットが返されます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "H5sWyu1kxnvg",
        "colab": {}
      },
      "source": [
        "features_dataset = tf.data.Dataset.from_tensor_slices((feature0, feature1, feature2, feature3))\n",
        "features_dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "m1C-t71Nywze",
        "colab": {}
      },
      "source": [
        "#  データセットから1つのサンプルだけを取り出すには`take(1)` を使います。\n",
        "for f0,f1,f2,f3 in features_dataset.take(1):\n",
        "    print(f0)\n",
        "    print(f1)\n",
        "    print(f2)\n",
        "    print(f3)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mhIe63awyZYd"
      },
      "source": [
        "`Dataset`のそれぞれの要素に関数を適用するには、`tf.data.Dataset.map`メソッドを使用します。\n",
        "\n",
        "マップされる関数はTensorFlowのグラフモードで動作する必要があります。関数は`tf.Tensors`を処理し、返す必要があります。`create_example`のような非テンソル関数は、互換性のため`tf.py_func`でラップすることができます。\n",
        "\n",
        "`tf.py_func`を使用する際には、シェイプと型は取得できないため、指定する必要があります。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "apB5KYrJzjPI",
        "colab": {}
      },
      "source": [
        "def tf_serialize_example(f0,f1,f2,f3):\n",
        "    tf_string = tf.py_func(\n",
        "        serialize_example, \n",
        "        (f0,f1,f2,f3),  # pass these args to the above function.\n",
        "        tf.string)      # the return type is `tf.string`.\n",
        "    return tf.reshape(tf_string, ()) # The result is a scalar"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CrFZ9avE3HUF"
      },
      "source": [
        "この関数をデータセットのそれぞれの要素に適用します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "VDeqYVbW3ww9",
        "colab": {}
      },
      "source": [
        "serialized_features_dataset = features_dataset.map(tf_serialize_example)\n",
        "serialized_features_dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "p6lw5VYpjZZC"
      },
      "source": [
        "TFRecordファイルに書き出します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "vP1VgTO44UIE",
        "colab": {}
      },
      "source": [
        "filename = 'test.tfrecord'\n",
        "writer = tf.data.experimental.TFRecordWriter(filename)\n",
        "writer.write(serialized_features_dataset)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6aV0GQhV8tmp"
      },
      "source": [
        "### TFRecordファイルの読み込み"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "o3J5D4gcSy8N"
      },
      "source": [
        "`tf.data.TFRecordDataset`クラスを使ってTFRecordファイルを読み込むこともできます。\n",
        "\n",
        "`tf.data`を使ってTFRecordファイルを取り扱う際の詳細については、[こちら](https://www.tensorflow.org/r1/guide/datasets#consuming_tfrecord_data)を参照ください。 \n",
        "\n",
        "`TFRecordDataset`を使うことは、入力データを標準化し、パフォーマンスを最適化するのに有用です。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "6OjX6UZl-bHC",
        "colab": {}
      },
      "source": [
        "filenames = [filename]\n",
        "raw_dataset = tf.data.TFRecordDataset(filenames)\n",
        "raw_dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6_EQ9i2E_-Fz"
      },
      "source": [
        "この時点で、データセットにはシリアライズされた`tf.train.Example`メッセージが含まれています。データセットをイテレートすると、スカラーの文字列テンソルが返ってきます。\n",
        "\n",
        "`.take`メソッドを使って最初の10レコードだけを表示します。\n",
        "\n",
        "注：`tf.data.Dataset`をイテレートできるのは、Eager Executionが有効になっている場合のみです。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "hxVXpLz_AJlm",
        "colab": {}
      },
      "source": [
        "for raw_record in raw_dataset.take(10):\n",
        "    print(repr(raw_record))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "W-6oNzM4luFQ"
      },
      "source": [
        "これらのテンソルは下記の関数でパースできます。\n",
        "\n",
        "注：ここでは、`feature_description`が必要です。データセットはグラフ実行を使用するため、この記述を使ってシェイプと型を構築するのです。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "zQjbIR1nleiy",
        "colab": {}
      },
      "source": [
        "# 特徴の記述\n",
        "feature_description = {\n",
        "    'feature0': tf.FixedLenFeature([], tf.int64, default_value=0),\n",
        "    'feature1': tf.FixedLenFeature([], tf.int64, default_value=0),\n",
        "    'feature2': tf.FixedLenFeature([], tf.string, default_value=''),\n",
        "    'feature3': tf.FixedLenFeature([], tf.float32, default_value=0.0),\n",
        "}\n",
        "\n",
        "def _parse_function(example_proto):\n",
        "  # 上記の記述を使って入力のtf.Exampleを処理\n",
        "  return tf.parse_single_example(example_proto, feature_description)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "gWETjUqhEQZf"
      },
      "source": [
        "あるいは、`tf.parse example`を使ってバッチ全体を一度にパースします。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "AH73hav6Bnmg"
      },
      "source": [
        "`tf.data.Dataset.map`メソッドを使って、データセットの各アイテムにこの関数を適用します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "6Ob7D-zmBm1w",
        "colab": {}
      },
      "source": [
        "parsed_dataset = raw_dataset.map(_parse_function)\n",
        "parsed_dataset "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "sNV-XclGnOvn"
      },
      "source": [
        "Eager Execution を使ってデータセット中の観測記録を表示します。このデータセットには10,000件の観測記録がありますが、最初の10個だけ表示します。  \n",
        "データは特徴量のディクショナリの形で表示されます。それぞれの項目は`tf.Tensor`であり、このテンソルの`numpy` 要素は特徴量を表します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "x2LT2JCqhoD_",
        "colab": {}
      },
      "source": [
        "for parsed_record in parsed_dataset.take(10):\n",
        "  print(repr(raw_record))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Cig9EodTlDmg"
      },
      "source": [
        "ここでは、`tf.parse_example` が`tf.Example`のフィールドを通常のテンソルに展開しています。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jyg1g3gU7DNn"
      },
      "source": [
        "## tf.python_ioを使ったTFRecordファイル"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3FXG3miA7Kf1"
      },
      "source": [
        "`tf.python_io`モジュールには、TFRecordファイルの読み書きのための純粋なPython関数も含まれています。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CKn5uql2lAaN"
      },
      "source": [
        "### TFRecordファイルの書き出し"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "LNW_FA-GQWXs"
      },
      "source": [
        "次にこの10,000件の観測記録を`test.tfrecords`ファイルに出力します。観測記録はそれぞれ`tf.Example`メッセージに変換され、ファイルに出力されます。その後、`test.tfrecords`ファイルが作成されたことを確認することができます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "MKPHzoGv7q44",
        "colab": {}
      },
      "source": [
        "# `tf.Example`観測記録をファイルに出力\n",
        "with tf.python_io.TFRecordWriter(filename) as writer:\n",
        "  for i in range(n_observations):\n",
        "    example = serialize_example(feature0[i], feature1[i], feature2[i], feature3[i])\n",
        "    writer.write(example)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "EjdFHHJMpUUo",
        "colab": {}
      },
      "source": [
        "!ls"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "wtQ7k0YWQ1cz"
      },
      "source": [
        "### TFRecordファイルの読み込み"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "utkozytkQ-2K"
      },
      "source": [
        "モデルに入力にするため、このデータを読み込みたいとしましょう。\n",
        "\n",
        "次の例では、データをそのまま、`tf.Example`メッセージとしてインポートします。これは、ファイルが期待されるデータを含んでいるかを確認するのに役に立ちます。これは、また、入力データがTFRecordとして保存されているが、[この](https://www.tensorflow.org/r1/guide/datasets#consuming_numpy_arrays)例のようにNumPyデータ（またはそれ以外のデータ型）として入力したい場合に有用です。このコーディング例では値そのものを読み取れるからです。\n",
        "\n",
        "入力ファイルの中のTFRecordをイテレートして、`tf.Example`メッセージを取り出し、その中の値を読み取って保存できます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "36ltP9B8OezA",
        "colab": {}
      },
      "source": [
        "record_iterator = tf.python_io.tf_record_iterator(path=filename)\n",
        "\n",
        "for string_record in record_iterator:\n",
        "  example = tf.train.Example()\n",
        "  example.ParseFromString(string_record)\n",
        "  \n",
        "  print(example)\n",
        "  \n",
        "  # Exit after 1 iteration as this is purely demonstrative.\n",
        "  # 純粋にデモであるため、イテレーションの1回目で終了\n",
        "  break"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "i3uquiiGTZTK"
      },
      "source": [
        "（上記で作成した`tf.Example`型の）`example`オブジェクトの特徴量は（他のプロトコルバッファメッセージと同様に）ゲッターを使ってアクセス可能です。`example.features`は`repeated feature`メッセージを返し、`feature`メッセージをを取得すると（Pythonのディクショナリとして保存された）特徴量の名前と特徴量の値のマッピングが得られます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "-UNzS7vsUBs0",
        "colab": {}
      },
      "source": [
        "print(dict(example.features.feature))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "u1M-WrbqUUVW"
      },
      "source": [
        "このディクショナリから、指定した値をディクショナリとして得ることができます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "2yCBu70IUb2H",
        "colab": {}
      },
      "source": [
        "print(example.features.feature['feature3'])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "4dw6_OI9UiNZ"
      },
      "source": [
        "次に、ゲッターを使って値にアクセスできます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "BdDYjDnDUlFe",
        "colab": {}
      },
      "source": [
        "print(example.features.feature['feature3'].float_list.value)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "S0tFDrwdoj3q"
      },
      "source": [
        "## ウォークスルー： 画像データの読み書き"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rjN2LFxFpcR9"
      },
      "source": [
        "以下は、TFRecordを使って画像データを読み書きする方法の例です。この例の目的は、データ（この場合は画像）を入力し、そのデータをTFRecordファイルに書き込んで、再びそのファイルを読み込み、画像を表示するという手順を最初から最後まで示すことです。\n",
        "\n",
        "これは、例えば、同じ入力データセットを使って複数のモデルを構築するといった場合に役立ちます。画像データをそのまま保存する代わりに、TFRecord形式に前処理しておき、その後の処理やモデル構築に使用することができます。\n",
        "\n",
        "まずは、雪の中の猫の[画像](https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg)と、ニューヨーク市にあるウイリアムズバーグ橋の [写真](https://upload.wikimedia.org/wikipedia/commons/f/fe/New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg)をダウンロードしましょう。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "5Lk2qrKvN0yu"
      },
      "source": [
        "### 画像の取得"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "3a0fmwg8lHdF",
        "colab": {}
      },
      "source": [
        "cat_in_snow  = tf.keras.utils.get_file('320px-Felis_catus-cat_on_snow.jpg', 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Felis_catus-cat_on_snow.jpg/320px-Felis_catus-cat_on_snow.jpg')\n",
        "williamsburg_bridge = tf.keras.utils.get_file('194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg','https://upload.wikimedia.org/wikipedia/commons/thumb/f/fe/New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg/194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "7aJJh7vENeE4",
        "colab": {}
      },
      "source": [
        "display.display(display.Image(filename=cat_in_snow))\n",
        "display.display(display.HTML('Image cc-by: <a \"href=https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg\">Von.grzanka</a>'))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "KkW0uuhcXZqA",
        "colab": {}
      },
      "source": [
        "display.display(display.Image(filename=williamsburg_bridge))\n",
        "display.display(display.HTML('<a \"href=https://commons.wikimedia.org/wiki/File:New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg\">source</a>'))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VSOgJSwoN5TQ"
      },
      "source": [
        "### TFRecordファイルの書き出し"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Azx83ryQEU6T"
      },
      "source": [
        "上記で行ったように、この特徴量を`tf.Example`と互換のデータ型にエンコードできます。この場合には、生画像のバイト文字列を特徴として保存するだけではなく、縦、横のサイズにチャネル数、更に画像を保存する際に猫の画像と橋の画像を区別するための`label`特徴量を付け加えます。猫の画像には`0`を、橋の画像には`1`を使うことにしましょう。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "kC4TS1ZEONHr",
        "colab": {}
      },
      "source": [
        "image_labels = {\n",
        "    cat_in_snow : 0,\n",
        "    williamsburg_bridge : 1,\n",
        "}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "c5njMSYNEhNZ",
        "colab": {}
      },
      "source": [
        "# 猫の画像を使った例\n",
        "image_string = open(cat_in_snow, 'rb').read()\n",
        "\n",
        "label = image_labels[cat_in_snow]\n",
        "\n",
        "# 関連する特徴量のディクショナリを作成\n",
        "def image_example(image_string, label):\n",
        "  image_shape = tf.image.decode_jpeg(image_string).shape\n",
        "\n",
        "  feature = {\n",
        "      'height': _int64_feature(image_shape[0]),\n",
        "      'width': _int64_feature(image_shape[1]),\n",
        "      'depth': _int64_feature(image_shape[2]),\n",
        "      'label': _int64_feature(label),\n",
        "      'image_raw': _bytes_feature(image_string),\n",
        "  }\n",
        "\n",
        "  return tf.train.Example(features=tf.train.Features(feature=feature))\n",
        "\n",
        "for line in str(image_example(image_string, label)).split('\\n')[:15]:\n",
        "  print(line)\n",
        "print('...')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2G_o3O9MN0Qx"
      },
      "source": [
        "ご覧のように、すべての特徴量が`tf.Example`メッセージに保存されました。上記のコードを関数化し、このサンプルメッセージを`images.tfrecords`ファイルに書き込みます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "qcw06lQCOCZU",
        "colab": {}
      },
      "source": [
        "# 生の画像をimages.tfrecordsファイルに書き出す\n",
        "# まず、2つの画像をtf.Exampleメッセージに変換し、\n",
        "# 次に.tfrecordsファイルに書き出す\n",
        "with tf.python_io.TFRecordWriter('images.tfrecords') as writer:\n",
        "  for filename, label in image_labels.items():\n",
        "    image_string = open(filename, 'rb').read()\n",
        "    tf_example = image_example(image_string, label)\n",
        "    writer.write(tf_example.SerializeToString())"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "yJrTe6tHPCfs",
        "colab": {}
      },
      "source": [
        "!ls"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jJSsCkZLPH6K"
      },
      "source": [
        "### TFRecordファイルの読み込み\n",
        "\n",
        "これで、`images.tfrecords`ファイルができました。このファイルの中のレコードをイテレートし、書き込んだものを読み出します。このユースケースでは、画像を復元するだけなので、必要なのは生画像の文字列だけです。上記のゲッター、すなわち、`example.features.feature['image_raw'].bytes_list.value[0]`を使って抽出することができます。猫と橋のどちらであるかを決めるため、ラベルも使用します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "M6Cnfd3cTKHN",
        "colab": {}
      },
      "source": [
        "raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')\n",
        "\n",
        "# 特徴量を記述するディクショナリを作成\n",
        "image_feature_description = {\n",
        "    'height': tf.FixedLenFeature([], tf.int64),\n",
        "    'width': tf.FixedLenFeature([], tf.int64),\n",
        "    'depth': tf.FixedLenFeature([], tf.int64),\n",
        "    'label': tf.FixedLenFeature([], tf.int64),\n",
        "    'image_raw': tf.FixedLenFeature([], tf.string),\n",
        "}\n",
        "\n",
        "def _parse_image_function(example_proto):\n",
        "  # 入力のtf.Exampleのプロトコルバッファを上記のディクショナリを使って解釈\n",
        "  return tf.parse_single_example(example_proto, image_feature_description)\n",
        "\n",
        "parsed_image_dataset = raw_image_dataset.map(_parse_image_function)\n",
        "parsed_image_dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "0PEEFPk4NEg1"
      },
      "source": [
        "TFRecordファイルから画像を復元しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "yZf8jOyEIjSF",
        "colab": {}
      },
      "source": [
        "for image_features in parsed_image_dataset:\n",
        "  image_raw = image_features['image_raw'].numpy()\n",
        "  display.display(display.Image(data=image_raw))"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}